/**
 * Crazy Table Box
 *  License by CrazyYi.
 *  v1.0.0 20200210
 *  v1.1.0 20200212: 增加支持datetime格式
 *  v1.1.1 20200214: 增加支持枚举类型
 *  v1.2.0 20200218: laydate增加支持范围
 *  v1.2.1 20200222: 当前列数据格式的转换
 *  v1.3.0 20200227: 优化box中数据和字典的对应关系
 *  v1.3.1 20200424: 针对select下拉框进行优化，增加默认的空选项(key='',value='')
 *  v1.3.2 20200503: 对于datetime 格式，单独处理
 *
 */
const modules = [
    'table', 'jquery', 'form', 'layer', 'laydate',
    'crazyTool', 'crazyQuery', 'crazyVerify', 'crazyHttp', 'crazyData', 'crazyDate'
];
layui.define(modules, function (exports) {
    const MOD_NAME = 'crazyBox'
    const TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
    let STR_CONN = ',';
    const SHOW_VIEW = 'crazy-table-show-view',
        SHOW_BOX = 'crazy-table-show-box',
        SHOW_DATE = 'crazy-table-show-datetime',

        SHOW_FILTER = 'crazy-table-show-form',
        SHOW_FORM_TEXT = 'showFormText',
        SHOW_FORM_CHOOSE = 'showFormChoose',

        BTN_SUBMIT = 'showBoxSubmit', // 按钮：提交
        BTN_CLOSED = 'showBoxClosed', // 按钮：关闭

        TEXT_BTN_SUBMIT = 'textFormSubmit', // text Form 提交按钮

        FORM_INPUT_SHOW = 'showBoxText', // 主form的text控件(显示值)
        FORM_INPUT_REQ = 'showBoxValue', // 主form的text控件(索引值)

        CHOOSE_FILTER = 'subFormFilter', // 所有subForm组件的过滤器
        CHOOSE_BTN_SUBMIT = 'subFormSubmit', // subForm 提交按钮

        HIDE = 'layui-hide',
        FORM_DATE = 'showBoxDatetime';

    const $ = layui.jquery,
        form = layui.form,
        laydate = layui.laydate,
        $Tool = layui.crazyTool,
        $Verify = layui.crazyVerify,
        $Table = layui.crazyQuery,
        $Data = layui.crazyData,
        $Http = layui.crazyHttp,
        $Date = layui.crazyDate

    const tipsOption = {
        title: undefined,
        color: "#7c7c7c",   // tips 背景色
        direct: 3, // 显示的方向，支持上右下左四个方向，通过1-4进行方向设定
        duration: 2000, // 自动关闭时间，默认2000ms，单位是ms。0表示不关闭
        delay: 500, // 鼠标悬停多久时间后进入tips
    };

    let $htmlBody = $('body'); // 在整个body添加;

    const defaultOptions = {
        cell: undefined, // 当前操作的单元格
        config: {}, // 当前操作列对应的配置项
        mode: undefined,
        value: undefined,
        newBtns: [],
        logOn: false,

        before: function (opt) {return {}}, // 在确认按钮提交前的处理，返回where条件。values:当前取值, cell:当前单元格, row:选中行数据
        submit: function (opt) { }, // values:当前取值, cell:当前单元格, row:当前行数据, config:当前配置
        closed: function (opt) { }, // values:当前取值, cell:当前单元格, row:当前行数据, config:当前配置
    };
    let gOptions = {};

    let showBoxIns = {};

    const gShowBox = {
        showBox: {}, // 弹出层（内含所有的过滤器）

        render: function (options) {
            console.log('--------- crazyTable Box ------------------');
            $.extend(gOptions, defaultOptions);
            $.extend(gOptions, options); // 注意：不能深度复制
            if (gOptions.config.type === 'datetime') {
                let fmt = gOptions.config.format || {};
                fmt.req = fmt.req || TIME_FORMAT;
                fmt.show = fmt.show || TIME_FORMAT;
                gOptions.config.format = fmt;
            }

            // gOptions.config.format = gOptions.config.format || (gOptions.config.type === 'datetime' ? TIME_FORMAT : undefined);
            // gOptions.config.dict = gOptions.config.dict.length ? gOptions.config.dict : gOptions.config.arrs;
            gOptions.config.name = gOptions.config.name || gOptions.config.field;
            gOptions.config.arrs = getColValues(gOptions.config);

            log.print('options init', gOptions);

            this.init();  // box初始化
            this.event(); // 定义相关事件
            return this.showBox;
        },
        /**
         * 计算 弹出层 的坐标
         * @returns {string}
         */
        elemXY: function () {
            let $thisElem = (gOptions.cell);
            // 计算弹出层的坐标
            let t = $thisElem.offset().top + $htmlBody.scrollTop() + $thisElem.outerHeight() + 5 + "px";
            let l = $thisElem.offset().left - ($htmlBody.outerWidth(true) - $htmlBody.outerWidth(true)) + "px";
            let w = gOptions.config.type === 'select' ? (250 + "px") : '';
            let s = `top:${t};left:${l};`;
            !!w ? s += `width:${w}` : '';
            return s;
        },
        /**
         * 创建本控件组
         * @returns {jQuery|HTMLElement}
         */
        create: function () {
            let _config = gOptions.config;
            let name = _config.name, type = _config.type;
            let boxId = gName.boxId(name);
            let style = this.elemXY(); // 计算弹出层的坐标
            let _div = `<div class="${SHOW_BOX}"><div id="${SHOW_FILTER}" class="" lay-filter="${SHOW_FILTER}" ></div></div>`;
            let showBox = $(`<div id="${boxId}" class="${SHOW_VIEW} ${type} layui-anim layui-anim-fadein" style="${style}">${_div}</div>`);

            return showBox;
        },
        /**
         * 更新config
         */
        configUpdate: function () {
            // let _config = gOptions.config;
            // log.print('field array', gOptions.config.arrs);
            // gOptions.config.dict = !!_config.dict && _config.dict.length > 0 ? _config.dict : _config.arrs; // 将当前列的所有值保存进来，便于后续使用
        },
        /**
         * 过滤器 弹出层
         * @returns {jQuery|HTMLElement}
         */
        init: function () {
            // 更新config字典
            this.configUpdate();

            // ===================================
            // 1.创建一个 弹出层
            let showBox = this.create();

            // 2.增加 一个text 控件模块
            showBox.find(`#${SHOW_FILTER}`).append(ul.create.text()); // 增加一个ul

            // 3.增加 按钮
            showBox.find(`#${SHOW_FILTER}`).append(button.create());

            // 4.在ul下面添加对应控件
            showBox.find('ul').append(elem.text.create());   // 添加一个 text Form：保存最终请求的数据
            showBox.find('ul').append(elem.choose.create()); // 增加一个 choose Form：当前进行的操作

            // 5.加入DOM
            $htmlBody.append(showBox);

            // 6.赋值FORM ?????
            form.val(SHOW_FORM_CHOOSE, gOptions.value);
            form.val(SHOW_FORM_TEXT, gOptions.value);

            // 7.执行一个laydate实例
            datetime.render();

            // 8.渲染layui form
            form.render(); // 全部form重新渲染

            // 9.渲染FORM 如果是searchInput 就默认选中
            let searchInput = showBox.find('form input[type="search"]');
            searchInput.focus().select();

            this.showBox = showBox;
        },
        /**
         * define all the button in the box
         */
        event: function () {
            let showBox = this.showBox;

            let othis = this;

            // 获取当前操作行
            let tr = $Table.getTrByCell(gOptions.cell);
            let row = {};
            if (typeof $(tr).attr('data-index') === 'undefined') {
                // 说明是 表头

            } else {
                let index = $(tr).attr('data-index');
                row = $Data.getRow(index);
            }

            // textForm 提交事件
            form.on(`submit(${TEXT_BTN_SUBMIT})`, function (data) {
                log.print('textForm button submit', data.field);
                let boxValues = getBoxValues(data.field); // 重构复选框结果
                log.print('textForm button submit, box values =', boxValues);
                if (boxValues === false) {
                    layer.msg('无法获取当前插件');
                    showBox.remove();
                    return;
                }

                // 先进行单元格校验
                if (!func.verifyCell(gOptions.cell, gOptions.config, boxValues.show)) {
                    log.print('validate cell fail')
                    return;
                } else {
                    gOptions.submit({cell: gOptions.cell, config: gOptions.config, values: boxValues, row: row}); // 回调函数
                }

                showBox.remove(); // 所有的event完成后，都需要关闭box
                return false;
            });

            // 点击确认
            form.on(`submit(${BTN_SUBMIT})`, function (data) {
                $(`#${TEXT_BTN_SUBMIT}`).trigger("click"); // 触发 textForm 提交
                return false; // form on click 后，需要return false
            });

            // 点击关闭
            form.on(`submit(${BTN_CLOSED})`, function (data) {
                log.print('form button click closed', data.field);

                showBox.remove();
                return false; // form on click 后，需要return false
            });

            // 点击其他区域关闭
            $(document).mouseup(function (e) {
                let userSet_con = $(`.${SHOW_VIEW}`);
                if (!userSet_con.is(e.target) && userSet_con.has(e.target).length === 0) {
                    showBox.remove();
                }
            });

            //===================================
            // 自定义按钮的点击事件
            //===================================
            $.each(gOptions.newBtns, function (i, btn) {
                let fn = gName.textBtnId(btn.filter); // textForm的button 的id，也是filter

                form.on(`submit(${btn.filter})`, function (data) {
                    $(`#${fn}`).trigger("click"); // 触发 textForm 的 id=fn 的按钮 提交
                    return false; // form on click 后，需要return false
                });

                form.on(`submit(${fn})`, function (data) {
                    let boxValues = getBoxValues(data.field); // 重构复选框结果
                    log.print('form button click ' + btn.filter + ', box values =', boxValues);
                    if (boxValues === false) {
                        layer.msg('无法获取当前插件');
                        showBox.remove();
                        return false;
                    }

                    btn.callback({cell: gOptions.cell, config: gOptions.config, values: boxValues, row: row}); // 调用自定义按钮的回调事件

                    showBox.remove(); // 所有的event完成后，都需要关闭box
                    return false; // form on click 后，需要return false
                });
            });

            button.mouseover(); // 给所有按钮添加 悬停tips效果
        },
    };

    /**
     * 将 form 中的提交的数据，进行预处理，使得符合后续要求的数据结构
     * @param dataField
     * @returns {*|boolean}
     */
    const getBoxValues = function (dataField) {
        log.print('调整前：', dataField)
        let boxValues = func.reset1(dataField); // 重构复选框结果
        log.print('调整中：', boxValues)
        boxValues = func.reset2(boxValues);
        log.print('调整后：', boxValues)
        return boxValues;
    };
    /**
     * 获取当前列的所有取值，并转变成数据字典
     * @param config
     * @returns {*|Array}
     */
    const getColValues = function (config) {
        let arrs = $Data.getColArray(config.field); // 获取当前列的值（去重），都是req
        switch (config.type) {
            case 'datetime':
                let fmt = config.format;
                let f = config.field;
                let n = config.name || config.field;
                // 如果 f == n，表示直接用的是数据表中的字段
                if (f === n) {
                    if (fmt.req === 'int') {
                        $.each(arrs, function (i, item) {
                            arrs[i].value = $Date.formatTime(new Date(item.value * 1000), fmt.show, '--')
                        });
                    } else {
                        $.each(arrs, function (i, item) {
                            arrs[i].value = $Date.formatTime($Date.getDateFromFmt(item.value, fmt.req), fmt.show, '--')
                        });
                    }
                } else {
                    // f != n，则 n 是数据表字段(req)，f 是转义的字段(show)
                    // 则：当前arrs 中的value 不变，key 需要转换
                    if (fmt.req === 'int') {
                        // key = 把value 转变成 时间戳
                        $.each(arrs, function (i, item) {
                            arrs[i].key = $Date.date2TimeStamp(item.value) / 1000;
                        });
                    } else {
                        // DO NOTHING...
                    }
                }
                // 接下来，将无效的时间格式取消掉
                let news = [];
                $.each(arrs, function (i, item) {
                    if (!(!item.key || item.value === '--')) {
                        news.push(item);
                    }
                });
                arrs = news;
                break;

            default:
                if (!!config.dict && $.isArray(config.dict) && config.dict.length > 0) {
                    $.each(arrs, function (i, item) {
                        $.each(config.dict, function (k, v) {
                            if ($Tool.compare(k, item.key, false)) {
                                arrs[i].value = v;
                                return false;
                            }
                        });
                    });
                }
        }

        return arrs;
    };

    /**
     * 内部函数
     * @type {{verifyCell: verifyCell, reset2: (function(*): *), reset1: (function(*): *)}}
     */
    const func = {
        verifyCell: function (cell, config, value) {
            // 获取校验规则、字段值
            let verifies = config.verify ? config.verify.split("|") : [];
            const callback = config.callback || false;

            let res;
            for (let j in verifies) {
                res = $Verify.verify(verifies[j], value, cell); // 进行校验，返回：校验失败错误信息，成功返回空字符串
                if (!res.res) {
                    callback && typeof callback === 'function' && callback(false, {
                        config: config,
                        cell: cell,
                        msg: res.msg
                    });
                    return false; // 返回校验失败
                }
            }

            // 校验成功
            callback && typeof callback === 'function' && callback(true, {
                config: config,
                cell: cell,
                msg: ''
            });
            return true;
        },

        reset1: function (values) {
            let c = gOptions.config;
            let n = c.name;
            let dict = c.dict.length ? c.dict : c.arrs;
            let newField = [];
            let isCheckbox = false;
            for (let key in values) {
                if (values.hasOwnProperty(key)) {
                    // 对于checkbox，由于form提交的数据是 field[name]格式，我们需要单独增加一个 field 字段
                    if (key.indexOf(n + '[') === 0) {
                        isCheckbox = true; // 说明找到了，当前是checkbox提交的数据
                        let k = key.substr((n + '[').length);
                        k = k.replace(']', ''); // 获取到实际的key值
                        let nO = {};
                        $.each(dict, function (i, item) {
                            if ($Tool.compare(item.key, k)) {
                                nO[k] = item.value;
                                return false;
                            }
                        });
                        newField.push(nO); // 对于checkbox，需要添加成数组
                        delete values[key];
                    }
                }
            }
            isCheckbox && newField.length > 0 ? values[n] = newField : ''; // 针对checkbox，需要单独保存一个新字段
            return values;
        },
        reset2: function (values) {
            let c = gOptions.config;
            let dict = c.dict.length ? c.dict : c.arrs,
                n = c.name || c.field;
            let show = [], req = [], curV = values[n];

            let f = function (item, dict) {
                if (item.constructor === Object) {
                    $.each(item, function (k, v) {
                        show.push(v);
                        req.push(k);
                    });
                } else {
                    $.each(dict, function (k, v) {
                        if (v.key + '' === curV + '') {
                            show.push(v.value);
                            req.push(v.key);
                        }
                    });
                    if (req.length === 0) {
                        show.push(curV);
                        req.push(curV);
                    }
                }
            };
            if (!!curV || curV == 0) {
                // 如果是数组，则说明是checkbox 传递过来的。其他情况下，是不会传递数组（这个是前提）
                if (Array.isArray(curV)) {
                    $.each(curV, function (i, item) {
                        f(item, dict)
                    });
                } else {
                    // 如果是 datetime，则处理方法不同，20200503
                    if (c.type === 'datetime') {
                        let fmt = c.format;
                        if (!!c.range || (curV + '').indexOf('~') > 0) {
                            // 如果 range 参数 存在
                            let t = curV.split('~')[0];
                            let range = c.range || 'date'; // 设置当前的range
                            let fmts = {
                                'year': 'yyyy',
                                'month': 'yyyy-MM',
                                'date': 'yyyy-MM-dd',
                                'time': 'HH:mm',
                                'datetime': 'yyyy-MM-dd HH:mm',
                            };
                            let showFmt = fmts[range]; // 当前显示的格式

                            let d = $Date.formatTime(new Date(t * 1000), showFmt, '--');
                            show.push(d);
                            req.push(curV);

                            console.log('cur show = ', show);
                            console.log('cur req = ', req);
                        } else {
                            // 如果 range 参数 不存在
                            if (fmt.req === 'int') {
                                show.push($Date.formatTime(new Date(curV * 1000), fmt.show, '--'));
                            } else {
                                show.push($Date.formatTime($Date.getDateFromFmt(curV, fmt.req), fmt.show, '--'));
                            }
                            req.push(curV);
                            // console.log('cur show = ', show);
                            // console.log('cur req = ', req);
                        }
                    } else {
                        f(curV, dict)
                    }
                }
            }

            values.show = show.join(STR_CONN);
            values.req = req.join(STR_CONN);
            return values;
        },

    };

    // =====================================
    // 各类子控件
    // =====================================

    const elem = {
        text: {
            create: function () {
                let v = gOptions.value, m = gOptions.mode, c = gOptions.config;
                let f = (c.type === 'datetime') ? formElem.create.datetime : formElem.create.input;
                let _i = f(c, v, m);

                const _class = 'layui-btn layui-btn-sm'
                let _btn = `<button id="${TEXT_BTN_SUBMIT}" type="button" class="${HIDE} layui-btn-normal" lay-submit lay-filter="${TEXT_BTN_SUBMIT}"></button>`;
                $.each(gOptions.newBtns, function (i, btn) {
                    let fn = gName.textBtnId(btn.filter);
                    _btn += `<button type="button" id="${fn}" class="${_class} ${HIDE} ${fn}" lay-submit lay-filter="${fn}"></button>`;
                });
                let _h = `<form class="layui-form ${SHOW_FILTER}" action="" lay-filter="${SHOW_FORM_TEXT}">${_i}${_btn}</form>`
                return _h;
            },
        },
        choose: {
            create: function () {
                let v = gOptions.value, m = gOptions.mode, c = gOptions.config;
                let f = formElem.create.input;
                switch (c.type) {
                    case 'input':
                        f = (m === 'edit') ? formElem.create.radio : formElem.create.checkbox;
                        break;
                    case 'datetime':
                        f = formElem.create.radio; // 当前时间字段，在filter下，先仅支持单选
                        break;
                    default:
                        f = formElem.create[c.type];
                }

                let _html = f(c, v, m);
                let _btn = `<button id="${CHOOSE_BTN_SUBMIT}" type="button" class="${HIDE} layui-btn-normal" lay-submit lay-filter="${CHOOSE_BTN_SUBMIT}"></button>`;
                _html = `<form class="layui-form ${SHOW_FILTER}" action="" lay-filter="${SHOW_FORM_TEXT}">${_html}${_btn}</form>`;
                return _html;
            },
        },
    };

    const button = {
        create: function () {
            let newBtns = gOptions.newBtns;
            const _class = 'layui-btn layui-btn-sm'
            let _html = '<div class="layui-btn-group">';
            _html += `<button type="button" class="${_class} layui-btn-normal ${BTN_SUBMIT}" lay-submit lay-filter="${BTN_SUBMIT}"><i class="layui-icon layui-icon-ok"></i></button>`;
            // 添加自定义 button
            $.each(newBtns, function (i, btn) {
                _html += `<button type="button" class="${_class} ${btn.class} ${btn.filter}" lay-submit lay-filter="${btn.filter}"><i class="layui-icon ${btn.icon}"></i></button>`;
            });
            _html += `<button type="button" class="${_class} layui-btn-primary ${BTN_CLOSED}" lay-submit lay-filter="${BTN_CLOSED}"><i class="layui-icon layui-icon-close"></i></button>`;
            _html += '</div>';
            return _html;
        },
        mouseover: function () {
            let othis = this;

            this.mouseoverEach(BTN_SUBMIT, '确定'); // 给当前按钮添加tips效果
            this.mouseoverEach(BTN_CLOSED, '关闭'); // 给当前按钮添加tips效果

            let newBtns = gOptions.newBtns;
            $.each(newBtns, function (i, btn) {
                othis.mouseoverEach(btn.filter, btn.tips);
            });
        },
        mouseoverEach: function (elem, title) {
            $Tool.mouseover($(`.${elem}`), title);
        },
    };

    const formElem = {
        create: {
            input: function (config, value, mode) {
                value = value || '';

                let name = config.name || config.field;
                let dict = config.dict.length ? config.dict : config.arrs;

                // 验证规则
                let verify = config.verify || '';
                let placeholder = config.placeholder || '关键词';
                let tips = config.verType || 'tips';
                let verifyHtml = `lay-verify="${verify}" lay-verType="${tips}" placeholder="${placeholder}"`;

                // 要把输入值，转变成：数组，或纯字符串（非数组型字符串），v1.3.0
                value = value.constructor === Array ? value :
                    value.constructor === Object ? (value[name] || '') :
                        (value.indexOf(STR_CONN) > -1 ? value.split(STR_CONN) : value);

                let curV = value, curK = value;
                if (config.dict.length > 0) {
                    // 如果dict存在，说明是枚举类型
                    if (config.type === 'select' || config.type === 'checkbox' || config.type === 'radio') {
                        if (config.field === config.name) {
                            // 说明当前传递的是dict 的key，我们需要提取对应的value 显示
                            curV = getValueByKey(config.dict, value);
                            curK = value;
                        } else {
                            // 说明当前传递的是dict 的value，则不需要做任何操作
                            curV = value;
                            curK = getKeyByValue(config.dict, curV);
                        }
                    }
                }

                let _html = '';
                switch (config.type) {
                    case 'radio':
                    case 'checkbox':
                        _html = `<input id="${FORM_INPUT_SHOW}" value="${curV}" class="layui-input" autocomplete="off" ${verifyHtml} readonly="readonly">`;
                        _html += `<input id="${FORM_INPUT_REQ}" name="${name}" value="${curK}" class="layui-input ${HIDE}" autocomplete="off">`;
                        break;

                    case 'select':
                        _html += `<input id="${FORM_INPUT_REQ}" name="${name}" value="${curK}" class="layui-input ${HIDE}" autocomplete="off">`;
                        break;

                    default:
                        _html = `<input type="search" id="${FORM_INPUT_REQ}" name="${name}" value="${curV}" class="layui-input" ${verifyHtml} autocomplete="off">`;
                }

                return _html;
            },
            checkbox: function (config, value, mode) {
                value = value || '';
                let name = config.name || config.field;
                let list = config.dict.length ? config.dict : config.arrs;

                let _html = '';
                let valueArr = value.constructor === Array ? value : value.constructor === Object ? value : value.split(STR_CONN);
                $.each(list, function (i, item) {
                    let isChecked = '';
                    item = $Tool.toObject(item); // 需要转换为对象{key:'',value:''}
                    $.each(valueArr, function (index, curV) {
                        if ($Tool.compare(item.key, curV)) {
                            isChecked = 'checked';
                            return false; //return false = break; return true = continue;
                        }
                    });
                    _html += `<li><input type="checkbox" name="${name}[${item.key}]" value="${item.key}" title="${item.value}" lay-skin="primary" ${isChecked} lay-filter="${CHOOSE_FILTER}"></li>`;
                });
                return _html;
            },
            radio: function (config, value, mode) {
                value = value || '';
                let name = config.name || config.field;
                let list = config.dict.length ? config.dict : config.arrs;

                let _html = '';
                $.each(list, function (i, item) {
                    item = $Tool.toObject(item);
                    let isChecked = $Tool.compare(item.value, value) ? 'checked' : '';
                    _html += `<li><input type="radio" name="${name}" value="${item.key}" title="${item.value}" lay-skin="primary" lay-filter="${CHOOSE_FILTER}" ${isChecked}></li>`;
                });
                return _html;
            },
            select: function (config, value, mode) {
                value = value || '';
                let name = config.name || config.field;
                let list = config.dict || config.arrs;
                let verify = config.verify || '';
                let tips = config.verType || 'tips';
                console.log('box select dict', list);

                let _option = ''; // 保存 select 中的option 代码
                let _hasSelected = false; // 判断当前是否有已经被选中的值
                // 开始构建 select 代码，v1.3.1
                let _html = `<select id="selectId" name="${name}" lay-verify="${verify}" lay-verType="${tips}" lay-filter="${CHOOSE_FILTER}" lay-search>`;
                $.each(list, function (i, item) {
                    let isSelected = $Tool.compare(item.value, value) ? 'selected' : '';
                    _hasSelected || (_hasSelected = $Tool.compare(item.value, value));

                    item = $Tool.toObject(item);
                    _option += `<option value="${item.key}" ${isSelected}>${item.value}</option>`;
                });

                _option = `<option value="">请选择或搜索</option>` + _option; // 添加一个默认选项，v1.3.1
                _html += _option + `</select>`;
                return _html;
            },
            datetime: function (config, value, mode) {
                // 初始化 laydate 显示
                value = value || '';
                let name = config.name || config.field;
                let fmt = config.format;
                if ((value + '').indexOf('~') > -1) {
                    value = (value + '').split('~')[0]; // 20200505，如果是范围，则选取第一个
                }
                if ((value + '').indexOf(',') > -1) {
                    value = (value + '').split(',')[0]; // 20200505，如果是数组，则选取第一个
                }

                let curD = (fmt.req === 'int') ? new Date(value * 1000) : new Date(value);
                // log.print('curr date', curD);
                // log.print('curr value', value);
                let curV = !value ? '' : $Date.formatTime(curD, fmt.show, ''); // 显示的值
                let curK = !value ? '' : $Date.formatTime(new Date(curV), fmt.req, ''); // 传递的值。记住：需要将 show的值重新转换成Date() 格式之后再转换
                log.print('curr value', curV, ', curr key', curK);

                let _html = `<input type="text" id="${FORM_INPUT_SHOW}" value="${curV}" class="layui-input layui-time-search" placeholder="请选择或输入时间">`;
                _html += `<input type="text" id="${FORM_INPUT_REQ}" name="${name}" value="${curK}" class="layui-input layui-time-search ${HIDE}">`;
                _html += `<div><input type="hidden" class="specific_value"><div class="${FORM_DATE}"></div></div>`;
                return _html;
            },
        },
    };

    /**
     * 通过 value，在数据字典dict中查找对应的 keys，v1.3.0
     * @param dict
     * @param value
     * @returns {boolean|*|string}
     */
    const getKeyByValue = function (dict, value) {
        value = value.constructor === Array ? value : value.indexOf(STR_CONN) ? value.split(STR_CONN) : value;
        if (dict.constructor !== Array || dict.length <= 0) {
            return false;
        }

        let keys = [];
        $.each(dict, function (i, item) {
            if (item.hasOwnProperty('key') && item.hasOwnProperty('value')) {
                if (value.constructor === Array) {
                    $.each(value, function (i, v) {
                        if (item.value + '' === v + '') {
                            keys.push(item.key);
                            return false; // break
                        }
                    });
                } else {
                    if (item.value + '' === value + '') {
                        keys.push(item.key);
                        return false; // break
                    }
                }
            }
        });
        return keys.join(STR_CONN);
    };

    /**
     * 通过 keys，在数据字典dict中查找对应的 values，v1.3.0
     * @param dict
     * @param keys
     * @returns {boolean|*|string}
     */
    const getValueByKey = function (dict, keys) {
        keys = keys.constructor === Array ? keys : keys.indexOf(STR_CONN) ? keys.split(STR_CONN) : keys;
        if (dict.constructor !== Array || dict.length <= 0) {
            return false;
        }

        let value = [];
        $.each(dict, function (i, item) {
            if (item.hasOwnProperty('key') && item.hasOwnProperty('value')) {
                if (keys.constructor === Array) {
                    $.each(keys, function (p, k) {
                        if (item.key + '' === k + '') {
                            value.push(item.value);
                            return false;
                        }
                    });
                } else {
                    if (item.key + '' === keys + '') {
                        value.push(item.value);
                        return false;
                    }
                }
            }
        });

        return value.join(STR_CONN);
    };

    const datetime = {
        /**
         * date 窗体初始化
         */
        render: function () {
            let _c = gOptions.config;
            if (_c.type !== 'datetime') {
                return;
            }
            log.print('datetime options =>', gOptions);

            // 执行一个laydate实例
            let id = gName.boxId(_c.name);
            let fmt = _c.format;
            let oldV = gOptions.value; // 原来的取值
            (fmt.req === 'int') && (oldV = oldV * 1000);
            oldV = $Date.formatTime(new Date(oldV), fmt.show);
            // log.print('cur init value:', oldV);

            // 针对显示出来的
            let fmtShow = fmt.show;
            let range = _c.range || false; // 范围

            laydate.render({
                elem: `#${id} .${FORM_DATE}`,  // 注意：这个是laydate 所在的div
                position: 'static',
                format: fmt.show, // 时间格式，具体参考：https://www.layui.com/doc/modules/laydate.html#format
                calendar: true,
                // value: '2020-02-16 18:00:00', // oldV, // 初始值
                done: function (value, date) {
                    // log.print('laydate done ', value);
                    // log.print('laydate date ', date);

                    let d = new Date(date.year, date.month - 1, date.date, date.hours, date.minutes, date.seconds);
                    let show = $Date.formatTime(d, fmt.show);               // 显示的值
                    // 如果是范围，则根据范围类型进行处理, v1.2
                    let reqV = '';
                    if (!range) {
                        reqV = $Date.formatTime(d, fmt.req);   // 传递的值。记住：需要将 show的值重新转换成Date() 格式之后再转换
                    } else {
                        let timeS, timeE;
                        switch (range) {
                            case 'month':
                                timeS = new Date(date.year, date.month - 1);
                                timeE = new Date(date.year, date.month - 1);
                                timeE.setMonth(timeE.getMonth() + 1);

                                break;
                            case 'date':
                                timeS = new Date(date.year, date.month - 1, date.date);
                                timeE = new Date(date.year, date.month - 1, date.date);
                                timeE.setDate(timeE.getDate() + 1);
                                break;
                            case 'hours':
                                timeS = new Date(date.year, date.month - 1, date.date, date.hours);
                                timeE = new Date(date.year, date.month - 1, date.date, date.hours);
                                timeE.setHours(timeE.getHours() + 1);
                                break;
                        }
                        timeE = timeE.getTime() - 1000;
                        timeE = new Date(timeE);
                        reqV = $Date.formatTime(timeS, fmt.req) + '~' + $Date.formatTime(timeE, fmt.req);
                    }

                    log.print('laydate reqV =', reqV, 'show=', show);

                    let $curr = $(`#${id}`);
                    $curr.find(`#${FORM_INPUT_SHOW}`).val(show);
                    $curr.find(`#${FORM_INPUT_REQ}`).val(reqV);
                }, // 控件选择完毕后的回调，点击日期、清空、现在、确定均会触发
                ready: function (date) {
                    // log.print(date); //得到初始的日期时间对象：{year: 2017, month: 8, date: 18, hours: 0, minutes: 0, seconds: 0}
                },
            });
            $('#' + id).addClass(SHOW_DATE); // 设置新的class
        },
    };

    const ul = {
        create: {
            text: function () {
                let name = gOptions.config.name, type = gOptions.config.type;
                return `<ul id="${gName.boxTextId(name)}" class="${type}"></ul>`
            },
            choose: function () {
                let name = gOptions.config.name, type = gOptions.config.type;
                return `<ul id="${gName.boxChooseId(name)}" class="${type}"></ul>`
            },
        },
    };

    // =====================================
    // 工具
    // =====================================

    /**
     * 工具
     * @type {{print: print}}
     */
    const log = {
        /**
         * 打印Log
         * @returns {boolean}
         */
        print: function () {
            let logOn = gOptions.logOn || false;
            if (!logOn) return false;
            const tips = 'crazyTable Box';
            console.log(tips + ' | ', arguments[0], arguments[1] || '',
                arguments[2] || '', arguments[3] || '', arguments[4] || '', arguments[5] || '');
            return true;
        },
    };

    /**
     * 全局需要用到的各种名称
     * @type {{textBtnId: (function(*): string), boxChooseId: (function(*=): string), boxTextId: (function(*=): string), btnSubmit: (function(*, *): string), boxId: (function(*): string)}}
     */
    const gName = {
        btnSubmit: function (field, index) {
            return `queryCondBtn_${field}_${index}`;
        },
        boxId: function (name) {
            return `mainTableBox_${name}`;
        },
        boxChooseId: function (name) {
            return `cond_choose_${this.boxId(name)}`
        },
        boxTextId: function (name) {
            return `cond_input_${this.boxId(name)}`
        },
        textBtnId: function (value) {
            return `textBtn_${value}`
        },
    };

    // =====================================
    // chooseForm 各类监听事件
    // =====================================

    // chooseForm radio 监听
    form.on(`radio(${CHOOSE_FILTER})`, function (data) {
        $(`#${CHOOSE_BTN_SUBMIT}`).trigger("click");
        return false;
    });
    // chooseForm select 监听
    form.on(`select(${CHOOSE_FILTER})`, function (data) {
        $(`#${CHOOSE_BTN_SUBMIT}`).trigger("click");
        return false;
    });
    // chooseForm checkbox 监听
    form.on(`checkbox(${CHOOSE_FILTER})`, function (data) {
        $(`#${CHOOSE_BTN_SUBMIT}`).trigger("click");
        return false;
    });
    // chooseForm submit 监听
    form.on(`submit(${CHOOSE_BTN_SUBMIT})`, function (data) {
        let boxValues = getBoxValues(data.field); // 重构复选框结果
        log.print('chooseForm button submit, box values =', boxValues);
        $(`#${FORM_INPUT_SHOW}`).val(boxValues.show); // 给show input赋值
        $(`#${FORM_INPUT_REQ}`).val(boxValues.req); // 给req input赋值
        return false;
    });

    const classIns = {
        render: function (options) {
            gShowBox.render(options);
        }
    };

    //输出接口
    exports(MOD_NAME, gShowBox);
});